﻿using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.Globalization;
using System.Linq;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Text;
using System.Text.RegularExpressions;
using System.Security.Permissions;
using NLog;

namespace Infinitus.Mail.Utilities.Helper
{
    #region Enums

    public enum DCItemType
    {
        OrganizationalUnit,
        Group,
        User,
    }

    #endregion

    public class DCHelper
    {
        
        #region Private fields

        private static readonly string[] _ouLoadProperties = new string[]{
                "objectGUID",
                "name",
                "adspath",
                "description",
                "displayName",
                "distinguishedName"
            };
        private static readonly string[] _userLoadProperties = new string[]{
                "objectGUID",
                "cn",// Same with CN
                "adspath",
                "memberof",
                "company",
                "department",
                "description",
                "displayname",
                "facsimiletelephonenumber",//Fax
                "givenname",//First Name
                "homephone",//Home Phone
                "ipphone",//IP Phone
                "l",//City
                "mail",
                "mailnickname",
                "manager",//Manager Distinguished Name
                "mobile",
                "otherhomephone",
                "othertelephone",
                "pager",
                "physicaldeliveryofficename",//Physical Delivery Office Name
                "postalcode",//Zip/Postal Code
                "postofficebox",//P.O.Box
                "samaccountname",
                "sn",//Last Name
                "st",//State/Province
                "streetaddress",//Street
                "telephonenumber",//Telephone Number
                "title",//Job Title
                "userprincipalname",
                "wwwhomepage",//Web Page
                "legacyexchangedn",
                "employeeID",
                "employeenumber",
                "userAccountControl", //state
                "extensionAttribute1", //UserNo
                "extensionAttribute2", //NickName
                "extensionAttribute3", //Position 
                "extensionAttribute4", //ORG_CODE
                "extensionAttribute5", //PART_TIME_ORG_CODE
                "extensionAttribute6", //UserName
                "distinguishedName",
                "msExchHideFromAddressLists",
                "thumbnailPhoto"
            };
        private static readonly string[] _groupLoadProperties = new string[]{
                "objectGUID",
                "cn",
                "samaccountname",
                "adspath",
                "mail",
                "member",
                "memberof",
                "legacyexchangedn",
                "description",
                "displayname",
                "extensionAttribute1", //OA_ID
                "extensionAttribute2", //ORDERED
                "extensionAttribute3", //LEADER_ACCOUNT
                "extensionAttribute4", //FULL_NAME
                "extensionAttribute5", //LEVEL
                "extensionAttribute6",  // PARENT_ID
                "distinguishedName",
                "msOrg-IsOrganizational"
            };
        const string _ouFilter = "(objectClass=OrganizationalUnit)";
        const string _userFilter = "(&(objectClass=user)(!objectClass=Computer))";
        const string _groupFilter = "(objectClass=Group)";

        private string _accountId = string.Empty;
        private string _password = string.Empty;
        private string[] _rootPaths = new string[0];
        private string _ldapRoot = string.Empty;
        private string _domainDCFormat = string.Empty;

        private Logger logger = LogManager.GetCurrentClassLogger();
        #endregion

        #region Constructor

        /// <summary>
        /// 初始化使用当前用户身份操作的AD代理类
        /// </summary>
        /// <param name="dcFQDN">DC的FQDN</param>
        /// <param name="domain">域名</param>
        /// <param name="rootOUPaths">组织架构根OU路径组，路径中OU层级以/分隔</param>
        public DCHelper(string dcFQDN, string domain, string[] rootOUPaths) : this(dcFQDN, domain, string.Empty, string.Empty, rootOUPaths) { }

        /// <summary>
        /// 初始化AD代理类
        /// </summary>
        /// <param name="dcFQDN">DC的FQDN</param>
        /// <param name="domain">域名</param>
        /// <param name="accountId">操作类所使用的用户Id</param>
        /// <param name="password">操作类所使用的用户密码</param>
        /// <param name="rootOUPaths">组织架构根OU路径组，路径中OU层级以/分隔</param>
        public DCHelper(string dcFQDN, string domain,
            string accountId, string password,
            string[] rootOUPaths)
        {
            #region Parameters check

            if (string.IsNullOrEmpty(dcFQDN))
            {
                throw new ArgumentNullException("dcFQDN");
            }
            if (string.IsNullOrEmpty(domain))
            {
                throw new ArgumentNullException("domain");
            }
            if ((!string.IsNullOrEmpty(accountId) || !string.IsNullOrEmpty(password))
                && (string.IsNullOrEmpty(accountId) || string.IsNullOrEmpty(password))
            )
            {
                throw new ArgumentException("参数“accountId”和“password”必须同时设置或同时置空。");
            }
            if (rootOUPaths == null || rootOUPaths.Length == 0)
            {
                throw new ArgumentNullException("rootOUPaths");
            }

            #endregion

            _ldapRoot = string.Format(CultureInfo.InvariantCulture, "LDAP://{0}/", dcFQDN);
            _domainDCFormat = string.Format(CultureInfo.InvariantCulture, "DC={0}", Regex.Replace(domain, "\\.", ",DC="));

            _rootPaths = new string[rootOUPaths.Length];
            for (int i = 0; i < rootOUPaths.Length; i++)
            {
                _rootPaths[i] = GetLdapPathFromItemPath(rootOUPaths[i], DCItemType.OrganizationalUnit);
            }
            _accountId = accountId;
            _password = password;

            logger.Trace("_accountId:{0}, _password:{1}, _ldapRoot:{2}, _domainDCFormat:{3}, ",
                _accountId, _password, _ldapRoot, _domainDCFormat);

            CheckValid();
        }

        #endregion

        #region DCItem Operation

        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        private Guid CreateItem(DCItem item
            , Action<DirectoryEntry, DCItem, object> updateItemAction
            , object tag)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
            if (string.IsNullOrEmpty(item.Name))
            {
                throw new ArgumentException("Property 'item.Name' is empty.");
            }
            if (string.IsNullOrEmpty(item.ParentLdapPath))
            {
                throw new ArgumentException("Property 'item.ParentLdapPath' is empty.");
            }
            if (updateItemAction == null)
            {
                throw new ArgumentNullException("updateItemAction");
            }

            DirectoryEntry de = GetDirectoryEntry(item.ParentLdapPath);
            DirectoryEntry itemEntry = de.Children.Add(GetItemPredicateFromType(item.ItemType)+ "=" + item.Name, GetSchemaClassNameFromItemType(item.ItemType));
            try
            {
                itemEntry.CommitChanges();
                item.ObjectGUID = GetItemGuidValue(itemEntry, "objectGUID");
                updateItemAction(itemEntry, item, tag);
                itemEntry.CommitChanges();
            }
            catch (DirectoryServicesCOMException dsEx)
            {
                if (dsEx.ErrorCode == -2147019886 && (dsEx.ExtendedError == 8305 || dsEx.ExtendedError == 1318 || dsEx.ExtendedError == 1316))//Exists.
                {
                    throw new InvalidOperationException("Item '" + item.Name + "' already exists.", dsEx);
                }
                else
                {
                    DeleteItem(item.ObjectGUID, item.ItemType, null);
                    throw;
                }
            }
            item.ObjectGUID = GetItemGuidValue(itemEntry, "objectGUID");
            item.LdapPath = itemEntry.Path;
            item.ParentLdapPath = GetParentLdapPath(itemEntry.Path);
            itemEntry.Dispose();
            itemEntry = null;
            de.Dispose();
            de = null;

            return item.ObjectGUID;
        }

        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        private Guid UpdateItem(DCItem item, Action<DirectoryEntry, DCItem, object> updateItemAction, object tag)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
            if (item.ObjectGUID == null || item.ObjectGUID == Guid.Empty)
            {
                throw new ArgumentException("Property 'item.ObjectGUID' is empty.");
            }
            if (string.IsNullOrEmpty(item.Name))
            {
                throw new ArgumentException("Property 'item.Name' is empty.");
            }
            if (string.IsNullOrEmpty(item.ParentLdapPath))
            {
                throw new ArgumentException("Property 'item.ParentLdapPath' is empty.");
            }
            if (updateItemAction == null)
            {
                throw new ArgumentNullException("updateItemAction");
            }

            DCItem tItem = GetItemById(item.ObjectGUID, item.ItemType);

            if (tItem == null)
            {
                throw new InvalidOperationException("Item '"+ item.Name +"' did not exists.");
            }
            else
            {
                using (DirectoryEntry de = GetDirectoryEntry(GetLdapPathFromDN(tItem.DistinguishedName)))
                {
                    updateItemAction(de, item, tag);
                    de.CommitChanges();
                }
                return tItem.ObjectGUID;
            }
        }

        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        private void MoveItem(DCItem item, string newParentLdapPath)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
            if (string.IsNullOrEmpty(item.LdapPath))
            {
                throw new ArgumentException("Property 'item.LdapPath' is empty.");
            }
            if (string.IsNullOrEmpty(newParentLdapPath))
            {
                throw new ArgumentNullException("newParentLdapPath");
            }

            using (DirectoryEntry de = GetDirectoryEntry(GetLdapPathFromDN(item.DistinguishedName)))
            {
                if (de != null && !de.Parent.Path.Equals(newParentLdapPath, StringComparison.CurrentCultureIgnoreCase))
                {
                    de.MoveTo(GetDirectoryEntry(newParentLdapPath));
                    de.CommitChanges();
                }
            }
        }

        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        private void DeleteItem(Guid itemId, DCItemType itemType, Func<DCItem, bool> canDeleteCheckFunc)
        {
            if (itemId == null || itemId.Equals(Guid.Empty))
            {
                throw new ArgumentNullException("itemId");
            }
            DCItem item = GetItemById(itemId, itemType);

            if (item != null && (canDeleteCheckFunc == null || canDeleteCheckFunc(item)))
            {
                using (DirectoryEntry de = GetDirectoryEntry(GetLdapPathFromDN(item.DistinguishedName)))
                {
                    de.Parent.Children.Remove(de);
                    de.CommitChanges();
                }
            }
        }

        #endregion

        #region OU Operation

        /// <summary>
        /// 获取顶层OU
        /// </summary>
        /// <returns>顶层OU数组</returns>
        public DCOU[] GetTopLevelDepartments()
        {
            List<DCOU> items = new List<DCOU>();
            foreach (var item in _rootPaths)
            {
                DCOU ou = GetOU(item);
                if (ou != null)
                {
                    items.Add(ou);
                }
            }
            return items.ToArray();
        }

        public DCGroup[] GetTopLevelGroups()
        {
            List<DCGroup> items = new List<DCGroup>();
            foreach (var item in _rootPaths)
            {
                items.AddRange( SearchGroup(item, SearchScope.OneLevel));
            }
            return items.ToArray();
        }

        public DCOU[] GetSubOUs(Guid departmentId)
        {
            DCOU dept = GetOU(departmentId);
            if (dept == null)
            {
                return new DCOU[0];
            }
            else
            {
                return GetSubOUs(dept);
            }
        }

        public DCOU[] GetSubOUs(DCOU parent)
        {
            if (parent == null)
            {
                throw new ArgumentNullException("parent");
            }

            return GetSubOUs(parent.LdapPath);
        }

        public DCOU[] GetSubOUs(string path)
        {
            return SearchOU(path, SearchScope.OneLevel);
        }

        public DCOU GetOU(Guid id)
        {
            return GetItemById(id, DCItemType.OrganizationalUnit) as DCOU;
        }

        public DCOU GetOU(string path)
        {
            DCOU[] ous = SearchOU(path, SearchScope.Base);
            return ous.Length == 0 ? null : ous[0];
        }

        public DCGroup[] GetOUGroups(Guid id)
        {
            DCOU ou = GetOU(id);
            if (ou == null)
            {
                return new DCGroup[0];
            }
            else
            {
                return GetOUGroups(ou);
            }
        }

        public DCGroup[] GetOUGroups(DCOU ou)
        {
            if (ou == null)
            {
                throw new ArgumentNullException("ou");
            }

            return SearchGroup(ou.LdapPath, SearchScope.OneLevel);
        }

        public DCUser[] GetOUUsers(Guid id)
        {
            DCOU ou = GetOU(id);
            if (ou == null)
            {
                return new DCUser[0];
            }
            else
            {
                return GetOUUsers(ou);
            }
        }

        public DCUser[] GetOUUsers(DCOU ou)
        {
            if (ou == null)
            {
                throw new ArgumentNullException("ou");
            }

            return SearchUser(ou.LdapPath, SearchScope.OneLevel);
        }

        public Guid CreateOU(DCOU ou)
        {
            return CreateItem(ou, UpdateOUAction, null);
        }

        public Guid UpdateOU(DCOU ou)
        {
            return UpdateItem(ou, UpdateOUAction, null);
        }

        public void MoveOU(DCOU ou, string newParentOUPath)
        {
            MoveItem(ou, newParentOUPath);
        }

        public void DeleteOU(Guid ouId)
        {
            DeleteItem(ouId, DCItemType.OrganizationalUnit, (item) =>
            {
                DCOU ou = item as DCOU;
                if (ou == null)
                {
                    return false;
                }
                else
                {
                    DCOU[] subOUs = GetSubOUs(ou);
                    DCGroup[] subGroups = GetOUGroups(ou);
                    DCUser[] subUsers = GetOUUsers(ou);
                    if ((subOUs != null && subOUs.Length != 0) || (subGroups != null && subGroups.Length != 0) || (subUsers != null && subUsers.Length != 0))
                    {
                        throw new Exception("成员不为空！");
                    }
                    return true;
                }
            });
        }

        private void UpdateOUAction(DirectoryEntry de, DCItem item, object tag)
        {
            DCOU ou = item as DCOU;
            if (ou != null)
            {
                de.Rename("OU=" + ou.Name);
                de.CommitChanges();
                SetItemValue(de, "description", ou.Description);
                SetItemValue(de, "displayName", ou.DisplayName);
            }
        }

        private DCOU[] SearchOU(string path, SearchScope scope)
        {
            return GetSearchResult<DCOU>(
                path,
                scope,
                string.Empty,
                DCItemType.OrganizationalUnit);
        }

        private DCOU GetOUFromSearchResult(
            SearchResult result)
        {
            if (result == null)
            {
                return null;
            }
            else
            {
                return new DCOU(GetItemValue(result, "name"), 
                    GetParentLdapPath(GetItemValue(result, "adspath")),
                    GetItemValue(result, "distinguishedName"))
                {
                    ObjectGUID = GetItemGuidValue(result, "objectGUID"),
                    Description = GetItemValue(result, "description"),
                    DisplayName = GetItemValue(result, "displayName"),
                };
            }
        }

        #endregion

        #region Group Operation

        private DCGroup[] SearchGroup(string path, SearchScope scope)
        {
            return GetSearchResult<DCGroup>(
                path,
                scope,
                string.Empty,
                DCItemType.Group
            );
        }

        private DCGroup GetGroupFromSearchResult(
            SearchResult result)
        {
            if (result == null)
            {
                return null;
            }
            else
            {
                return new DCGroup(GetItemValue(result, "samaccountname"),
                    GetItemValue(result, "cn"),
                    GetParentLdapPath(GetItemValue(result, "adspath")),
                    GetItemValue(result, "distinguishedName"))
                {
                    ObjectGUID = GetItemGuidValue(result, "objectGUID"),
                    Email = GetItemValue(result, "mail"),
                    MemberOf = GetItemValues(result, "memberof"),
                    Members = GetItemValues(result, "member"),
                    DisplayName = GetItemValue(result, "displayname"),
                    Description = GetItemValue(result, "description"),
                    LegacyExchangeDN = GetItemValue(result, "legacyexchangedn"),
                    OaId = GetItemValue(result, "extensionAttribute1"),
                    Ordered = GetItemValue(result, "extensionAttribute2"),
                    LeaderAccount = GetItemValue(result, "extensionAttribute3"),
                    FullName = GetItemValue(result, "extensionAttribute4"),
                    Level = GetItemValue(result, "extensionAttribute5"),
                    ParentId = GetItemValue(result, "extensionAttribute6"),
                    MsOrgIsOrganizational = (!string.IsNullOrEmpty(GetItemValue(result, "msOrg-IsOrganizational")) ? Convert.ToBoolean(GetItemValue(result, "msOrg-IsOrganizational")) : false)
                };
            }
        }

        private void UpdateGroupAction(DirectoryEntry de, DCItem item, object tag)
        {
            DCGroup group = item as DCGroup;
            if (group != null)
            {
                de.Rename("CN=" + group.Name);
                de.CommitChanges();
                SetItemValue(de, "sAMAccountName", group.AccountId);
                SetItemValue(de, "displayname", group.DisplayName);
                SetItemValue(de, "description", group.FullName);
                SetItemValue(de, "mail", group.Email);
                SetItemValue(de, "extensionAttribute1", group.OaId);
                SetItemValue(de, "extensionAttribute2", group.Ordered);
                SetItemValue(de, "extensionAttribute3", group.LeaderAccount);
                SetItemValue(de, "extensionAttribute4", group.FullName);
                SetItemValue(de, "extensionAttribute5", group.Level);
                SetItemValue(de, "extensionAttribute6", group.ParentId);
                SetItemValue(de, "msOrg-IsOrganizational", group.MsOrgIsOrganizational);
                //de.Properties["groupType"].Value = -2147483640; //通用+安全组
                SetItemValue(de, "groupType", 8); //通用+通讯组
            }
        }


        public DCGroup GetGroup(Guid id)
        {
            return GetItemById(id, DCItemType.Group) as DCGroup;
        }

        public DCGroup GetGroup(string path)
        {
            DCGroup[] groups = SearchGroup(path, SearchScope.Base);
            return groups.Length == 0 ? null : groups[0];
        }

        public DCGroup[] GetSubGroups(Guid groupId)
        {
            return GetSubGroups(GetGroup(groupId));
        }
        
        public DCGroup[] GetSubGroups(DCGroup group)
        {
            if (group == null)
            {
                return new DCGroup[0];
            }
            else
            {
                return GetGroupsFromDistinguishedNameArray(group.Members);
            }
        }

        public DCUser[] GetGroupUsers(Guid groupId)
        {
            return GetGroupUsers(GetGroup(groupId));
        }

        public DCUser[] GetGroupUsers(DCGroup group)
        {
            if (group == null)
            {
                return new DCUser[0];
            }
            else
            {
                return GetUsersFromDistinguishedNameArray(group.Members);
            }
        }

        public Guid CreateGroup(DCGroup group)
        {
            return CreateItem(group, UpdateGroupAction, null);
        }

        public Guid UpdateGroup(DCGroup group)
        {
            return UpdateItem(group, UpdateGroupAction, null);
        }

        public void MoveGroup(DCGroup group, string newParentOUPath)
        {
            MoveItem(group, newParentOUPath);
        }

        public void DeleteGroup(Guid groupId)
        {
            DeleteItem(groupId, DCItemType.Group, (item) =>
            {
                DCGroup group = GetGroup(groupId);
                if (group != null && group.Members != null && group.Members.Length != 0)
                {
                    throw new Exception("成员不为空！");
                }
                return true;
            });
        }

        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        public void AddGroupMember(string groupPath, string memberPath)
        {
            if (string.IsNullOrEmpty(groupPath) ||
                string.IsNullOrEmpty(memberPath) ||
                groupPath.Equals(memberPath, StringComparison.OrdinalIgnoreCase))
            {
                return;
            }

            using (DirectoryEntry de = GetDirectoryEntry(groupPath))
            {
                string[] members = GetItemValues(de, "member");
                if (!members.Contains(GetDNFromLdapPath(memberPath)))
                {
                    de.Path = groupPath;
                    de.Invoke("Add", new object[] { memberPath });
                    de.CommitChanges();
                }
            }
        }

        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        public void RemoveGroupMember(string groupPath, string memberPath)
        {
            if (string.IsNullOrEmpty(groupPath) ||
                string.IsNullOrEmpty(memberPath) ||
                groupPath.Equals(memberPath, StringComparison.InvariantCultureIgnoreCase))
            {
                return;
            }

            using (DirectoryEntry de = GetDirectoryEntry(groupPath))
            {
                de.Path = groupPath;
                de.Invoke("Remove", new Object[] { memberPath });
                de.CommitChanges();
            }
        }

        #endregion

        #region User Operation

        private DCUser[] SearchUser(string path, SearchScope scope)
        {
            return GetSearchResult<DCUser>(
                path,
                scope,
                string.Empty,
                DCItemType.User
            );
        }

        private DCUser GetUserFromSearchResult(SearchResult result)
        {
            if (result == null)
            {
                return null;
            }
            else
            {
                DCUser user = new DCUser(GetItemValue(result, "sAMAccountName"),
                    GetItemValue(result, "cn"),
                    GetParentLdapPath(GetItemValue(result, "adspath")),
                    GetItemValue(result, "userprincipalname"),
                    GetItemValue(result, "distinguishedName"))
                {
                    ObjectGUID = GetItemGuidValue(result, "objectGUID"),
                    FirstName = GetItemValue(result, "givenname"),
                    LastName = GetItemValue(result, "sn"),
                    DisplayName = GetItemValue(result, "displayName"),
                    LegacyExchangeDN = GetItemValue(result, "legacyexchangedn"),
                    Description = GetItemValue(result, "description"),
                    MemberOf = GetItemValues(result, "memberof"),
                    Email = GetItemValue(result, "mail"),
                    MailNickName = GetItemValue(result, "mailNickName"),
                    Company = GetItemValue(result, "company"),
                    Department = GetItemValue(result, "department"),
                    ManagerDistinguishedName = GetItemValue(result, "manager"),
                    JobTitle = GetItemValue(result, "title"),
                    Office = GetItemValue(result, "physicaldeliveryofficename"),
                    Telephone = GetItemValue(result, "telephonenumber"),
                    Fax = GetItemValue(result, "facsimiletelephonenumber"),
                    Mobile = GetItemValue(result, "mobile"),
                    IPPhone = GetItemValue(result, "ipphone"),
                    HomePhone = GetItemValue(result, "homephone"),
                    Pager = GetItemValue(result, "pager"),
                    OtherTelephone = GetItemValue(result, "othertelephone"),
                    OtherHomePhone = GetItemValue(result, "otherhomephone"),
                    WebPage = GetItemValue(result, "wwwhomepage"),
                    Province = GetItemValue(result, "st"),
                    City = GetItemValue(result, "l"),
                    Street = GetItemValue(result, "streetaddress"),
                    POBox = GetItemValue(result, "postofficebox"),
                    PostalCode = GetItemValue(result, "postalcode"),
                    EmployeeID = GetItemValue(result, "employeeID"),
                    EmployeeNumber = GetItemValue(result, "employeenumber"),
                    ThumbnailPhoto = (result != null && !string.IsNullOrEmpty("thumbnailPhoto") && result.Properties.Contains("thumbnailPhoto")) ? (byte[])result.Properties["thumbnailPhoto"][0] : null, 
                    UserNo = GetItemValue(result, "extensionAttribute1"),
                    NickName = GetItemValue(result, "extensionAttribute2"),
                    Position = GetItemValue(result, "extensionAttribute3"),
                    OrgCode = GetItemValue(result, "extensionAttribute4"),
                    PartTimeOrgCode = GetItemValue(result, "extensionAttribute5"),
                    UserName = GetItemValue(result, "extensionAttribute6"),
                    UserAccountControl = Convert.ToInt32(GetItemValue(result, "userAccountControl")),
                    Disabled = (int.Parse(result.GetDirectoryEntry().Properties["userAccountControl"].Value.ToString()) & 2) == 2,
                };
                try
                {
                    user.MsExchHideFromAddressLists = (!string.IsNullOrEmpty(GetItemValue(result, "msExchHideFromAddressLists")) ? Convert.ToBoolean(GetItemValue(result, "msExchHideFromAddressLists")) : false);
                }
                catch (Exception ex)
                {
                    logger.ErrorException("GetUserFromSearchResult", ex);
                }
                return user;
            }
        }

        private void UpdateUserAction(DirectoryEntry de, DCItem item, object tag)
        {
            DCUser user = item as DCUser;
            string password = tag as string;
            if (user != null)
            {
                SetItemValue(de, "sAMAccountName", user.AccountId);
                de.Properties["userPrincipalName"].Add(user.UserPrincipalName);
                de.CommitChanges();

                try
                {
                    de.Rename("CN=" + user.Name);
                    de.CommitChanges();
                }
                catch (Exception ex)
                {
                    logger.ErrorException(string.Format("忽略de.Rename(CN={0})时出现的警告", user.Name), ex);
                }
                //Set password
                if (!string.IsNullOrEmpty(password))
                {   
                    //新增用户时
                    de.Invoke("SetPassword", new object[] { password });
                    de.Properties["pwdlastset"].Value = 0; //用户下次登录时须更改密码

                    try
                    {
                        de.CommitChanges();
                    }
                    catch (Exception ex)
                    {
                        if (ex.InnerException != null)
                        {
                            throw ex.InnerException;
                        }
                        else if (ex != null && !string.IsNullOrEmpty(ex.Message))
                        {
                            throw ex;
                        }
                    }

                    try
                    {
                        de.Properties["userAccountControl"].Value = 512; //NORMAL_ACCOUNT
                        //de.Properties["userAccountControl"].Value = 66048;//DONT_EXPIRE_PASSWORD + NORMAL_ACCOUNT

                        SetUserCanChangePwdProperty(de, true);

                        de.CommitChanges();
                    }
                    catch (Exception ex)
                    {
                        if (ex.InnerException != null)
                        {
                            throw ex.InnerException;
                        }
                        else
                        {
                            throw;
                        }
                    }
                }

                SetItemValue(de, "displayname", user.DisplayName);
                SetItemValue(de, "mail", user.Email);
                SetItemValue(de, "mailNickname", user.MailNickName);
                SetItemValue(de, "givenname", user.FirstName);
                SetItemValue(de, "sn", user.LastName);
                SetItemValue(de, "description", user.Description);
                SetItemValue(de, "company", user.Company);
                SetItemValue(de, "title", user.JobTitle);
                SetItemValue(de, "physicaldeliveryofficename", user.Office);
                SetItemValue(de, "manager", user.ManagerDistinguishedName);
                SetItemValue(de, "department", user.Department);
                SetItemValue(de, "telephonenumber", user.Telephone);
                SetItemValue(de, "facsimiletelephonenumber", user.Fax);
                SetItemValue(de, "mobile", user.Mobile);
                SetItemValue(de, "ipphone", user.IPPhone);
                SetItemValue(de, "homephone", user.HomePhone);
                SetItemValue(de, "pager", user.Pager);
                SetItemValue(de, "othertelephone", user.OtherTelephone);
                SetItemValue(de, "otherhomephone", user.OtherHomePhone);
                SetItemValue(de, "wwwhomepage", user.WebPage);
                SetItemValue(de, "st", user.Province);
                SetItemValue(de, "l", user.City);
                SetItemValue(de, "streetaddress", user.Street);
                SetItemValue(de, "postofficebox", user.POBox);
                SetItemValue(de, "postalcode", user.PostalCode);
                SetItemValue(de, "employeeNumber", user.EmployeeNumber);
                SetItemValue(de, "employeeID", user.UserNo);
                SetItemValue(de, "extensionAttribute1", user.UserNo);
                SetItemValue(de, "extensionAttribute2", user.NickName);
                SetItemValue(de, "extensionAttribute3", user.Position);
                SetItemValue(de, "extensionAttribute4", user.OrgCode);
                SetItemValue(de, "extensionAttribute5", user.PartTimeOrgCode);
                SetItemValue(de, "extensionAttribute6", user.UserName);
                de.CommitChanges();
            }
        }

        

        public DCUser[] SearchUsers(string keyword)
        {
            if (string.IsNullOrEmpty(keyword))
            {
                return new DCUser[0];
            }

            List<DCUser> userList = new List<DCUser>();
            foreach (var item in _rootPaths)
            {
                userList.AddRange(GetSearchResult<DCUser>(
                    item,
                    SearchScope.Subtree,
                    string.Format(CultureInfo.InvariantCulture,
                        "(|(sAMAccountName=*{0}*)(displayname=*{0}*)(mail=*{0}*))", keyword),
                    DCItemType.User
                    ));
            }
            return userList.ToArray();
        }

        public DCUser GetUserByAccountId(string accountId)
        {
            if (string.IsNullOrEmpty(accountId))
            {
                return null;
            }

            DCUser[] result = new DCUser[0];
            foreach (var item in _rootPaths)
            {
                result = GetSearchResult<DCUser>(
                    item,
                    SearchScope.Subtree,
                    string.Format(CultureInfo.InvariantCulture,
                        "(sAMAccountName={0})", accountId),
                    DCItemType.User
                    );
                if (result != null && result.Length == 1)
                {
                    break;
                }
            }

            if (result != null && result.Length == 1)
            {
                return result[0];
            }
            else
            {
                return null;
            }
        }


        public DCUser GetUserByUserNo(string userNo)
        {
            if (string.IsNullOrEmpty(userNo))
            {
                return null;
            }

            DCUser[] result = new DCUser[0];
            foreach (var item in _rootPaths)
            {
                result = GetSearchResult<DCUser>(
                    item,
                    SearchScope.Subtree,
                    string.Format(CultureInfo.InvariantCulture,
                        "(extensionAttribute1={0})", userNo),
                    DCItemType.User
                    );
                if (result != null && result.Length > 0)
                {
                    break;
                }
            }

            if (result != null && result.Length > 0)
            {
                return result[0];
            }
            else
            {
                return null;
            }

        }

        public DCUser GetUser(Guid id)
        {
            return GetItemById(id, DCItemType.User) as DCUser;
        }

        public DCUser GetUser(string path)
        {
            DCUser[] users = SearchUser(path, SearchScope.Base);
            return users.Length == 0 ? null : users[0];
        }

        public Guid CreateUser(DCUser user, string password)
        {
            return CreateItem(user, UpdateUserAction, password);
        }

        public Guid UpdateUser(DCUser user)
        {
            return UpdateItem(user, UpdateUserAction, null);
        }

        public void MoveUser(DCUser user, string newParentOUPath)
        {
            MoveItem(user, newParentOUPath);
        }

        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        public void DisableUser(DCUser user)
        {
            if (user == null)
            {
                return;
            }

            using (DirectoryEntry de = GetDirectoryEntry(GetLdapPathFromDN(user.DistinguishedName)))
            {
                int old = int.Parse(de.Properties["userAccountControl"].Value.ToString());
                de.Properties["userAccountControl"].Value = old | 2;
                de.CommitChanges();
            }
        }

        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        public void EnableUser(DCUser user)
        {
            if (user == null)
            {
                return;
            }

            using (DirectoryEntry de = GetDirectoryEntry(GetLdapPathFromDN(user.DistinguishedName)))
            {
                int old = int.Parse(de.Properties["userAccountControl"].Value.ToString());
                de.Properties["userAccountControl"].Value = (old | 2) - 2;
                de.CommitChanges();
            }
        }

        //[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        //public void DeleteUser(DCUser user)
        //{
        //    if (user == null)
        //    {
        //        return;
        //    }

        //    DirectoryEntry de = GetDirectoryEntry(user.LdapPath);
        //    de.Parent.Children.Remove(de);
        //    de.CommitChanges();

        //    if (de != null)
        //    {
        //        de.Dispose();
        //    }
        //}

        #endregion

        #region Private share functions
        
        private void CheckValid()
        {
            using (DirectoryEntry de = GetDirectoryEntry(_rootPaths[0]))
            {
                Exception exct = null;
                try
                {
                    GetOU(_rootPaths[0]);
                }
                catch (UnauthorizedAccessException uaEx)
                {
                    exct = uaEx;
                }
                catch (Exception ex)
                {
                    exct = ex;
                }
                finally
                {
                    if (exct != null)
                    {
                        string checkFaildMessage = string.Empty;
                        if (exct.InnerException != null && !string.IsNullOrEmpty(exct.InnerException.Message))
                        {
                            checkFaildMessage = exct.InnerException.Message;
                        }
                        else if (!string.IsNullOrEmpty(exct.Message))
                        {
                            checkFaildMessage = exct.Message;
                        }
                        else
                        {
                            checkFaildMessage = exct.ToString();
                        }
                        throw new Exception(checkFaildMessage);
                    }
                }
            }
        }


        #endregion

        public DCGroup[] GetGroupMemberOf(DCGroup group)
        {
            if (group == null)
            {
                return new DCGroup[0];
            }
            else
            {
                return GetGroupsFromDistinguishedNameArray(group.MemberOf);
            }
        }

        public DCGroup[] GetUserMemberOf(DCUser user)
        {
            if (user == null)
            {
                return new DCGroup[0];
            }
            else
            {
                return GetGroupsFromDistinguishedNameArray(user.MemberOf);
            }
        }

        public DCUser GetUserManager(DCUser user)
        {
            if (user == null || string.IsNullOrEmpty(user.ManagerDistinguishedName))
            {
                return null;
            }
            else
            {
                return GetUser(GetLdapPathFromDN(user.ManagerDistinguishedName));
            }
        }

        public void MoveGroupMemberOf(Guid groupId, Guid moveToGroupId)
        {
            DCGroup group = GetGroup(groupId);
            DCGroup newGroup = GetGroup(moveToGroupId);
            if (group == null || newGroup == null)
            {
                return;
            }

            string[] memberof = group.MemberOf;
            foreach (var item in memberof)
            {
                RemoveGroupMember(GetLdapPathFromDN(item), group.LdapPath);
            }
            AddGroupMember(GetGroup(moveToGroupId).LdapPath, group.LdapPath);
        }

        public void AddUserToGroup(DCUser user, Guid groupId)
        {
            DCGroup group = GetGroup(groupId);
            if (group == null || user == null)
            {
                return;
            }
            AddGroupMember(GetPath(group.LdapPath), GetPath(GetLdapPathFromDN(user.DistinguishedName)));
        }

        public void RemoveUserFromGroup(DCUser user, Guid groupId)
        {
            DCGroup group = GetGroup(groupId);
            if (group == null || user == null)
            {
                return;
            }
            RemoveGroupMember(GetPath(group.LdapPath), GetPath(GetLdapPathFromDN(user.DistinguishedName)));
        }

        private DCGroup[] GetGroupsFromDistinguishedNameArray(string[] distinguishedNameArray)
        {
            if (distinguishedNameArray == null || distinguishedNameArray.Length == 0)
            {
                return new DCGroup[0];
            }
            else
            {
                List<DCGroup> groups = new List<DCGroup>();
                for (int i = 0; i < distinguishedNameArray.Length; i++)
                {
                    DCGroup group = GetGroup(GetLdapPathFromDN(distinguishedNameArray[i]));
                    if (group != null)
                    {
                        groups.Add(group);
                    }
                }
                return groups.ToArray();
            }
        }

        private DCUser[] GetUsersFromDistinguishedNameArray(string[] distinguishedNameArray)
        {
            if (distinguishedNameArray == null || distinguishedNameArray.Length == 0)
            {
                return new DCUser[0];
            }
            else
            {
                List<DCUser> users = new List<DCUser>();
                for (int i = 0; i < distinguishedNameArray.Length; i++)
                {
                    DCUser user = GetUser(GetLdapPathFromDN(distinguishedNameArray[i]));
                    if (user != null)
                    {
                        users.Add(user);
                    }
                }
                return users.ToArray();
            }
        }

        private DirectoryEntry GetDirectoryEntry(string path)
        {
            if (string.IsNullOrEmpty(path))
            {
                throw new ArgumentNullException("path");
            }

            path = GetPath(path);

            if (!string.IsNullOrEmpty(_accountId) && !string.IsNullOrEmpty(_password))
            {
                return new DirectoryEntry(path, _accountId, _password, AuthenticationTypes.Secure);
            }
            else
            {
                return new DirectoryEntry(path);
            }
        }

        private static void SetItemValue(DirectoryEntry de, string propertyName, object propertyValue)
        {
            if (de != null && !string.IsNullOrEmpty(propertyName))
            {
                if (propertyValue != null)
                {
                    if (de.Properties.Contains(propertyName))
                    {
                        de.Properties[propertyName][0] = propertyValue;
                    }
                    else if ((!(propertyValue is string) && propertyValue != null) || (propertyValue is string && !string.IsNullOrEmpty((string)propertyValue)))
                    {
                        de.Properties[propertyName].Add(propertyValue);
                    }
                }
                else
                {
                    if (de.Properties.Contains(propertyName))
                    {
                        de.Properties[propertyName].Value = null;
                        de.Properties[propertyName].Clear();
                    }
                }
            }
        }

        private static void SetItemValue(DirectoryEntry de, string propertyName, string[] propertyValue)
        {
            if (de != null && !string.IsNullOrEmpty(propertyName) && propertyValue != null && propertyValue.Length != 0)
            {
                for (int i = 0; i < propertyValue.Length; i++)
                {
                    if (de.Properties.Contains(propertyName))
                    {
                        de.Properties[propertyName][i] = propertyValue[i];
                    }

                    else
                    {
                        de.Properties[propertyName].Add(propertyValue[i]);
                    }
                }
            }
        }

        private DCItem GetItemByName(string name, DCItemType itemType, string parentLdapPath = "")
        {
            return GetItem(itemType, string.Format(
                        CultureInfo.CurrentCulture,
                        "(name={0})", name));
        }

        private DCItem GetItemById(Guid id, DCItemType itemType)
        {
            return GetItem(itemType, string.Format(
                        CultureInfo.CurrentCulture,
                        "(objectGUID={0})", GetByteArrayStringFromGuid(id)));
        }

        private DCItem GetItem(DCItemType itemType, string condition, string parentLdapPath = "")
        {
            List<string> path = new List<string>();
            if (!string.IsNullOrEmpty(parentLdapPath))
            {
                path.Add(parentLdapPath);
            }
            else
            {
                path.AddRange(_rootPaths);
            }
            foreach (var item in path)
            {
                DCItem[] result = GetSearchResult<DCItem>(
                    item,
                    SearchScope.Subtree,
                    string.IsNullOrEmpty(condition) ? string.Empty : condition,
                    itemType);
                if (result != null && result.Length == 1)
                {
                    return result[0];
                }
            }

            return null;
        }

        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        public T[] GetSearchResult<T>(string path, SearchScope scope, string filter, DCItemType itemType) where T: class
        {
            string itemFilter = string.Empty;
            string[] loadProperties = new string[0];
            Func<SearchResult, DCItem> getEntityFromResultHandler = null;
            switch (itemType)
            {
                case DCItemType.OrganizationalUnit:
                    itemFilter = _ouFilter;
                    loadProperties = _ouLoadProperties;
                    getEntityFromResultHandler = GetOUFromSearchResult;
                    break;
                case DCItemType.Group:
                    itemFilter = _groupFilter;
                    loadProperties = _groupLoadProperties;
                    getEntityFromResultHandler = GetGroupFromSearchResult;
                    break;
                case DCItemType.User:
                    itemFilter = _userFilter;
                    loadProperties = _userLoadProperties;
                    getEntityFromResultHandler = GetUserFromSearchResult;
                    break;
            }
            if (!string.IsNullOrEmpty(filter))
            {
                itemFilter = string.Format(
                        CultureInfo.CurrentCulture,
                        "(&{0}{1})", itemFilter, filter);
            }


            List<DCItem> items = new List<DCItem>();
            DirectoryEntry de = GetDirectoryEntry(path);
            //logger.Trace("OriPath {1} TracferPath {1}", path, GetPath(path));
            using (DirectorySearcher searcher = new DirectorySearcher(de))
            {
                searcher.SearchScope = scope;
                searcher.Filter = itemFilter;
                searcher.PageSize = 1000;
                if (loadProperties != null && loadProperties.Length != 0)
                {
                    searcher.PropertiesToLoad.AddRange(loadProperties);
                }
                try
                {
                    SearchResultCollection results = searcher.FindAll();
                    if (getEntityFromResultHandler != null)
                    {
                        foreach (SearchResult result in results)
                        {
                            items.Add(getEntityFromResultHandler(result));
                        }
                    }
                }
                catch (DirectoryServicesCOMException dsEx)
                {
                    if (dsEx.ErrorCode == -2147016656 && dsEx.ExtendedError == 8333)// Ldap path did not exists.
                    {

                    }
                    else
                    {
                        throw;
                    }
                }
                de = null;
            }
            T[] ra = new T[items.Count];
            for (int i = 0; i < items.Count; i++)
            {
                ra[i] = items[i] as T;
            }
            return ra;
        }

        private string GetPath(string path)
        {
            return Regex.Replace(path, @"OU=[.\w+\/.\w+]+\,", o => o.Value.Replace(@"/", @"\/"));
        }


        public T[] GetAll<T>(DCItemType dcItemType) where T : class
        {
            List<T> list = new List<T>();
            foreach (var path in _rootPaths)
            {
                list.AddRange(GetSearchResult<T>(path, SearchScope.Subtree, string.Empty, dcItemType));
            }
            return list.ToArray();
        }


        private static string GetSchemaClassNameFromItemType(DCItemType itemType)
        {
            return Enum.GetName(typeof(DCItemType), itemType).ToLowerInvariant();
        }

        private static DirectoryEntry GetDirectoryEntryFromSearchResult(
            SearchResult result)
        {
            return result == null ? null : result.GetDirectoryEntry();
        }

        private static string GetByteArrayStringFromGuid(Guid guid)
        {
            byte[] ba = guid.ToByteArray();
            StringBuilder sb = new StringBuilder();
            foreach (var item in ba)
            {
                sb.Append("\\").Append(item.ToString("X").ToUpperInvariant());
            }
            return sb.ToString();
        }

        #region PathConvert
        
        public static string GetParentLdapPath(string ldapPath)
        {
            return Regex.Replace(ldapPath, "/(ou|cn)=.*?," , "/",
                RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase);
        }

        public static string GetItemNameFromLdapPath(string ldapPath)
        {
            Match m = Regex.Match(ldapPath, "/(ou|cn)=(?<itemName>.*?),",
                RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase);
            return m.Success ? m.Groups["itemName"].Value : string.Empty;
        }

        public string GetLdapPathFromItemPath(string itemPath, DCItemType itemType)
        {
            return string.Format(
                    CultureInfo.InvariantCulture,
                    "{0}{1}",
                    _ldapRoot,
                    GetDNFromItemPath(itemPath, itemType));
        }

        public string GetDNFromItemPath(string itemPath, DCItemType itemType)
        {
            if (string.IsNullOrEmpty(itemPath))
            {
                return string.Empty;
            }
            string[] ouPaths = itemPath.Split('/');
            if (ouPaths.Length == 0)
            {
                return string.Empty;
            }

            StringBuilder newOUName = new StringBuilder();
            string smPre = "OU=";
            for (int i = ouPaths.Length - 1; i >= 0; i--)
            {
                if (i == 0 && itemType != DCItemType.OrganizationalUnit) smPre = "CN=";
                if (!string.IsNullOrEmpty(ouPaths[i]))
                {
                    newOUName.Append(smPre + Regex.Replace(ouPaths[i], @"([+="";<>\,/\\])", @"\${1}") + ",");
                }
            }

            return newOUName.ToString() + _domainDCFormat;
        }

        public static string GetItemPathFromLdapPath(string ldapPath)
        {
            return GetItemPathFromDN(GetDNFromLdapPath(ldapPath));
        }

        public static string GetItemPathFromDN(string dn)
        {
            MatchCollection mc = Regex.Matches(dn, @"(OU|CN)=(?<item>.*?),");
            string[] ous = new string[mc.Count];
            for (int i = mc.Count - 1; i >= 0; i--)
            {
                ous[mc.Count - 1 - i] = mc[i].Groups["item"].Value;
            }
            return string.Join("/", ous);
        }

        public static string GetDNFromLdapPath(string ldapPath)
        {
            return Regex.Replace(ldapPath, "LDAP://.*?/", "",
                RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase);
        }

        public string GetLdapPathFromDN(string distinguishedName)
        {
            return string.Format(CultureInfo.InvariantCulture, @"{0}{1}", _ldapRoot, distinguishedName);
        }

        public static string GetItemPredicateFromType(DCItemType itemType)
        {
            return itemType == DCItemType.OrganizationalUnit ? "OU" : "CN";
        }

        #endregion

        private static void SetUserCanChangePwdProperty(DirectoryEntry de, bool canChangePwd)
        {
            Guid changePasswordGuid = new Guid("{ab721a53-1e2f-11d0-9819-00aa0040529b}");
            IdentityReference selfSddl = new SecurityIdentifier(WellKnownSidType.WorldSid, null); // or  ("S-1-1-0");
            ActiveDirectoryAccessRule adar = GetActiveDirectoryAccessRule(de, selfSddl, changePasswordGuid);
            if (adar != null)
            {
                de.ObjectSecurity.RemoveAccessRule(adar);
            }

            ActiveDirectoryAccessRule selfAccRule = new ActiveDirectoryAccessRule(selfSddl, ActiveDirectoryRights.ExtendedRight, canChangePwd ? AccessControlType.Allow : AccessControlType.Deny, changePasswordGuid);
            de.ObjectSecurity.AddAccessRule(selfAccRule);
        }

        private static ActiveDirectoryAccessRule GetActiveDirectoryAccessRule(DirectoryEntry de, IdentityReference identity, Guid objectType)
        {
            int findCount = 0;
            ActiveDirectoryAccessRule returnValue = null;
            foreach (var item in de.ObjectSecurity.GetAccessRules(true, true, typeof(SecurityIdentifier)))
            {
                ActiveDirectoryAccessRule rule = item as ActiveDirectoryAccessRule;
                if (rule != null)
                {
                    if (rule.IdentityReference == identity && rule.ObjectType == objectType)
                    {
                        findCount++;
                        returnValue = rule;
                    }
                }
            }
            return returnValue;
        }

        #region GetItemValue

        private static Guid GetItemGuidValue(SearchResult item, string propertyName)
        {
            return item != null && !string.IsNullOrEmpty(propertyName) && item.Properties.Contains(propertyName) ?
                new Guid((byte[])item.Properties[propertyName][0]) :
                Guid.Empty;
        }

        private static Guid GetItemGuidValue(DirectoryEntry item, string propertyName)
        {
            return item != null && !string.IsNullOrEmpty(propertyName) && item.Properties.Contains(propertyName) ?
                new Guid((byte[])item.Properties[propertyName][0]) :
                Guid.Empty;
        }

        private static string GetItemValue(SearchResult item, string propertyName)
        {
            return item != null && !string.IsNullOrEmpty(propertyName) && item.Properties.Contains(propertyName) ?
                item.Properties[propertyName][0].ToString() :
                string.Empty;
        }

        private static string GetItemValue(DirectoryEntry item, string propertyName)
        {
            return item != null && !string.IsNullOrEmpty(propertyName) && item.Properties.Contains(propertyName) ?
                item.Properties[propertyName][0].ToString() :
                string.Empty;
        }

        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        private static string[] GetItemValues(SearchResult item, string propertyName)
        {
            if (item == null || string.IsNullOrEmpty(propertyName) || !item.Properties.Contains(propertyName))
            {
                return new string[0];
            }
            else
            {
                string[] result = new string[item.Properties[propertyName].Count];
                for (int i = 0; i < result.Length; i++)
                {
                    result[i] = item.Properties[propertyName][i].ToString();
                }
                return result;
            }
        }

        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        private static string[] GetItemValues(DirectoryEntry item, string propertyName)
        {
            if (item == null || string.IsNullOrEmpty(propertyName) || !item.Properties.Contains(propertyName))
            {
                return new string[0];
            }
            else
            {
                string[] result = new string[item.Properties[propertyName].Count];
                for (int i = 0; i < result.Length; i++)
                {
                    result[i] = item.Properties[propertyName][i].ToString();
                }
                return result;
            }
        }


    }
    #endregion

    #region Entity Definition

    #region DCItem

    /// <summary>
    /// DC通用对象类，具体DC类的父类
    /// </summary>
    public abstract class DCItem
    {
        /// <summary>
        /// 对象唯一编号
        /// </summary>
        public Guid ObjectGUID { get; set; }

        public string Base64ObjectGuid
        {
            get
            {
                return Convert.ToBase64String(ObjectGUID.ToByteArray());
            }
        }

        /// <summary>
        /// 对象名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 对象显示名称
        /// </summary>
        public string DisplayName { get; set; }

        /// <summary>
        /// 对象描述
        /// </summary>
        public string Description { get; set; }

        /// <summary>
        /// 对象路径
        /// </summary>
        public string LdapPath { get; set; }

        /// <summary>
        /// 父对象路径。对于OU对象，为父OU路径；对于其他对象，为所属OU路径
        /// </summary>
        public string ParentLdapPath { get; set; }

        public string LegacyExchangeDN { get; set; }

        public string DistinguishedName { get; set; }
        /// <summary>
        /// 对象类型
        /// </summary>
        public DCItemType ItemType { get; protected set; }

        public DCItem(DCItemType itemType, string name, string parentLdapPath, string distinguishedName)
        {
            ItemType = itemType;
            Name = name;
            ParentLdapPath = parentLdapPath;
            LdapPath = Regex.Replace(parentLdapPath, @"(ldap://.*?/)(.*)", "$1"+
                DCHelper.GetItemPredicateFromType(ItemType) + "=" + name +
                ",$2", RegexOptions.IgnoreCase | RegexOptions.Singleline);
            DistinguishedName = distinguishedName;

            ObjectGUID = Guid.Empty;
            DisplayName = string.Empty;
            Description = string.Empty;
            LegacyExchangeDN = string.Empty;
        }
    }

    #endregion

    #region DCOU

    public class DCOU : DCItem
    {
        public DCOU(string name, string parentLdapPath, string distinguishedName)
            : base(DCItemType.OrganizationalUnit, name, parentLdapPath, distinguishedName) { }
    }

    #endregion

    #region DCGroup

    public class DCGroup : DCItem
    {
        public string AccountId { get; set; }

        public string Email { get; set; }

        public string[] Members { get; set; }

        public string[] MemberOf { get; set; }

        public string OaId { get; set; }

        public string Ordered { get; set; }
        
        public string LeaderAccount { get; set; }

        public string FullName { get; set; }

        public string Level { get; set; }

        public string ParentId { get; set; }

        public bool MsOrgIsOrganizational { get; set; }

        public DCGroup(string accountId, string name, string parentLdapPath, string distinguishedName)
            : base(DCItemType.Group, name, parentLdapPath, distinguishedName)
        {
            AccountId = accountId;

            Email = string.Empty;
            MemberOf = new string[0];
            Members = new string[0];
        }

    }

    #endregion

    #region DCUser

    public class DCUser : DCItem
    {
        private string _accountId;
        public string AccountId
        {
            get
            {
                if (string.IsNullOrEmpty(_accountId))
                    return null;
                return _accountId;
            }
            set { _accountId = value; }
        }

        private string _userPrincipalName;
        public string UserPrincipalName
        {
            get
            {
                if (string.IsNullOrEmpty(_userPrincipalName))
                    return null;
                return _userPrincipalName;
            }
            set { _userPrincipalName = value; }
        }


        private string _firstName;
        public string FirstName
        {
            get
            {
                if (string.IsNullOrEmpty(_firstName))
                    return null;
                return _firstName;
            }
            set { _firstName = value; }
        }


        private string _lastName;
        public string LastName
        {
            get
            {
                if (string.IsNullOrEmpty(_lastName))
                    return null;
                return _lastName;
            }
            set { _lastName = value; }
        }


        public string[] MemberOf { get; set; }

        private string _email;
        public string Email
        {
            get
            {
                if (string.IsNullOrEmpty(_email))
                    return null;
                return _email;
            }
            set { _email = value; }
        }


        private string _mailNickName;
        public string MailNickName
        {
            get
            {
                if (string.IsNullOrEmpty(_mailNickName))
                    return null;
                return _mailNickName;
            }
            set { _mailNickName = value; }
        }

        private string _company;
        public string Company
        {
            get
            {
                if (string.IsNullOrEmpty(_company))
                    return null;
                return _company;
            }
            set { _company = value; }
        }

        private string _department;
        public string Department
        {
            get
            {
                if (string.IsNullOrEmpty(_department))
                    return null;
                return _department;
            }
            set { _department = value; }
        }

        private string _managerDistinguishedName;
        public string ManagerDistinguishedName
        {
            get
            {
                if (string.IsNullOrEmpty(_managerDistinguishedName))
                    return null;
                return _managerDistinguishedName;
            }
            set { _managerDistinguishedName = value; }
        }


        private string _jobTitle;
        public string JobTitle
        {
            get
            {
                if (string.IsNullOrEmpty(_jobTitle))
                    return null;
                return _jobTitle;
            }
            set { _jobTitle = value; }
        }


        private string _office;
        public string Office
        {
            get
            {
                if (string.IsNullOrEmpty(_office))
                    return null;
                return _office;
            }
            set { _office = value; }
        }

        private string _telephone;
        public string Telephone
        {
            get
            {
                if (string.IsNullOrEmpty(_telephone))
                    return null;
                return _telephone;
            }
            set { _telephone = value; }
        }


        private string _fax;
        public string Fax
        {
            get
            {
                if (string.IsNullOrEmpty(_fax))
                    return null;
                return _fax;
            }
            set { _fax = value; }
        }

        private string _mobile;
        public string Mobile
        {
            get
            {
                if (string.IsNullOrEmpty(_mobile))
                    return null;
                return _mobile;
            }
            set { _mobile = value; }
        }

        private string _iPPhone;
        public string IPPhone
        {
            get
            {
                if (string.IsNullOrEmpty(_iPPhone))
                    return null;
                return _iPPhone;
            }
            set { _iPPhone = value; }
        }

        private string _homePhone;
        public string HomePhone
        {
            get
            {
                if (string.IsNullOrEmpty(_homePhone))
                    return null;
                return _homePhone;
            }
            set { _homePhone = value; }
        }

        private string _pager;
        public string Pager
        {
            get
            {
                if (string.IsNullOrEmpty(_pager))
                    return null;
                return _pager;
            }
            set { _pager = value; }
        }

        private string _otherTelephone;
        public string OtherTelephone
        {
            get
            {
                if (string.IsNullOrEmpty(_otherTelephone))
                    return null;
                return _otherTelephone;
            }
            set { _otherTelephone = value; }
        }

        private string _otherHomePhone;
        public string OtherHomePhone
        {
            get
            {
                if (string.IsNullOrEmpty(_otherHomePhone))
                    return null;
                return _otherHomePhone;
            }
            set { _otherHomePhone = value; }
        }

        private string _webPage;
        public string WebPage
        {
            get
            {
                if (string.IsNullOrEmpty(_webPage))
                    return null;
                return _webPage;
            }
            set { _webPage = value; }
        }

        private string _province;
        public string Province
        {
            get
            {
                if (string.IsNullOrEmpty(_province))
                    return null;
                return _province;
            }
            set { _province = value; }
        }


        private string _city;
        public string City
        {
            get
            {
                if (string.IsNullOrEmpty(_city))
                    return null;
                return _city;
            }
            set { _city = value; }
        }

        private string _street;
        public string Street
        {
            get
            {
                if (string.IsNullOrEmpty(_street))
                    return null;
                return _street;
            }
            set { _street = value; }
        }

        private string _pOBox;
        public string POBox
        {
            get
            {
                if (string.IsNullOrEmpty(_pOBox))
                    return null;
                return _pOBox;
            }
            set { _pOBox = value; }
        }

        private string _postalCode;
        public string PostalCode
        {
            get
            {
                if (string.IsNullOrEmpty(_postalCode))
                    return null;
                return _postalCode;
            }
            set { _postalCode = value; }
        }


        private string _employeeNumber;
        public string EmployeeNumber
        {
            get
            {
                if (string.IsNullOrEmpty(_employeeNumber))
                    return null;
                return _employeeNumber;
            }
            set { _employeeNumber = value; }
        }

        private string _employeeID;
        public string EmployeeID
        {
            get
            {
                if (string.IsNullOrEmpty(_employeeID))
                    return null;
                return _employeeID;
            }
            set { _employeeID = value; }
        }

        private string _userNo;
        public string UserNo
        {
            get
            {
                if (string.IsNullOrEmpty(_userNo))
                    return null;
                return _userNo;
            }
            set { _userNo = value; }
        }

        private string _nickName;
        public string NickName
        {
            get
            {
                if (string.IsNullOrEmpty(_nickName))
                    return null;
                return _nickName;
            }
            set { _nickName = value; }
        }


        private string _position;
        public string Position
        {
            get
            {
                if (string.IsNullOrEmpty(_position))
                    return null;
                return _position;
            }
            set { _position = value; }
        }


        private string _orgCode;
        public string OrgCode
        {
            get
            {
                if (string.IsNullOrEmpty(_orgCode))
                    return null;
                return _orgCode;
            }
            set { _orgCode = value; }
        }

        private string _partTimeOrgCode;
        public string PartTimeOrgCode
        {
            get
            {
                if (string.IsNullOrEmpty(_partTimeOrgCode))
                    return null;
                return _partTimeOrgCode;
            }
            set { _partTimeOrgCode = value; }
        }

        private bool _disabled;
        public bool Disabled
        {
            get { return _disabled; }
            set { _disabled = value; }
        }


        private string _userName;
        public string UserName
        {
            get
            {
                if (string.IsNullOrEmpty(_userName))
                    return null;
                return _userName;
            }
            set { _userName = value; }
        }

        private bool _msExchHideFromAddressLists;
        public bool MsExchHideFromAddressLists
        {
            get { return _msExchHideFromAddressLists; }
            set { _msExchHideFromAddressLists = value; }
        }

        private int _userAccountControl;
        public int UserAccountControl
        {
            get { return _userAccountControl; }
            set { _userAccountControl = value; }
        }

        private byte[] _thumbnailPhoto;
        public byte[] ThumbnailPhoto
        {
            get { return _thumbnailPhoto; }
            set { _thumbnailPhoto = value; }
        }

        public DCUser(string accountId, string name, string parentLdapPath, string userPrincipalName, string distinguishedName)
            : base(DCItemType.User, name, parentLdapPath, distinguishedName)
        {
            AccountId = accountId;
            UserPrincipalName = userPrincipalName;

            City = string.Empty;
            Company = string.Empty;
            Department = string.Empty;
            Email = string.Empty;
            Fax = string.Empty;
            FirstName = string.Empty;
            HomePhone = string.Empty;
            IPPhone = string.Empty;
            JobTitle = string.Empty;
            LastName = string.Empty;
            ManagerDistinguishedName = string.Empty;
            MemberOf = new string[0];
            Mobile = string.Empty;
            Office = string.Empty;
            OtherHomePhone = string.Empty;
            OtherTelephone = string.Empty;
            Pager = string.Empty;
            POBox = string.Empty;
            PostalCode = string.Empty;
            Province = string.Empty;
            Street = string.Empty;
            Telephone = string.Empty;
            WebPage = string.Empty;
        }


    }

    #endregion

    #endregion

}